package it.eng.ws.test.automata;

import it.eng.ws.test.configuration.ConfigurationManager;
import it.eng.ws.test.exceptions.UAConfigurationException;
import it.eng.ws.test.exceptions.UAExecutionException;
import it.eng.ws.test.logging.LoggingSample;

import java.util.ArrayList;
import java.util.Iterator;

import org.apache.log4j.Logger;

import cern.jet.random.Uniform;

public class State {

	private ArrayList<StateAction> stateActions = null;
	private ArrayList<StateLink> stateLinks = null;
	private String label;
	private boolean initialState = false;
	private boolean finalState = false;
	private static Logger l = null;

	public State(){
		stateActions = new ArrayList<StateAction>();
		stateLinks = new ArrayList<StateLink>();
		if (l == null)
			l = LoggingSample.getLogger(it.eng.ws.test.automata.State.class);
	}

	public String getLabel() {
		return label;
	}

	public void setLabel(String label) {
		this.label = label;
	}

	public boolean isInitialState() {
		return initialState;
	}

	public void setInitialState(boolean initialState) {
		this.initialState = initialState;
	}

	public boolean isFinalState() {
		return finalState;
	}

	public void setFinalState(boolean finalState) {
		this.finalState = finalState;
	}

	public void addStateAction(StateAction action){
		stateActions.add(action);
	}

	public void addStateLink(StateLink link){
		stateLinks.add(link);
	}

	public void execute() throws UAExecutionException {
		for (Iterator<StateAction> actions = stateActions.iterator(); actions.hasNext();){
			StateAction action = actions.next();
			l.info(action.getClass().getName());
			action.doAction();
		}

	}

	public State switchToNextState() throws UAExecutionException {

		try {
			Uniform uniform = ConfigurationManager.getInstance().getUDRandomGenerator();
			double random = uniform.nextDouble();

			double bottom = 0.0d;
			boolean found = false;
			StateLink targetLink = null;
			for (Iterator<StateLink> stateLinkIterator = stateLinks.iterator(); stateLinkIterator.hasNext() && !found;){
				targetLink = stateLinkIterator.next();
				if (random >= bottom && random < bottom + targetLink.getLikelyhood())
					found = true;
				else bottom += bottom + targetLink.getLikelyhood();
			}

			int delay = targetLink.getPDRandomGenerator().nextInt();
		//	l.info("Attendo "+delay/1000+" secondi...");
			Thread.sleep(delay);

			return Automata.getInstance().getState(targetLink.getTargetState());

		} catch (Exception e) {
			throw new UAExecutionException(e);
		} 

	}

	public void checkConsistency() throws UAConfigurationException{

		if (!this.isFinalState() && stateLinks.size() == 0)
			throw new UAConfigurationException("Non-final State " + getLabel() + " has no outbound links.");

		if (this.isFinalState() && stateLinks.size() > 0)
			throw new UAConfigurationException("Final State " + getLabel() + " has outbound links.");

		float sum = 0;

		StateLink stateLink = null;

		if (!this.isFinalState()){
			Iterator<StateLink> stateLinkIterator = stateLinks.iterator();
			while(stateLinkIterator.hasNext()){
				stateLink = stateLinkIterator.next();

				sum += stateLink.getLikelyhood();
				if (Automata.getInstance().getState(stateLink.getTargetState())==null)
					throw new UAConfigurationException("Outbound state link from state " + getLabel() + " refers unexisting state ("+stateLink.getTargetState()+")");
			}

			if (sum!=1)
				throw new UAConfigurationException("Outbound state link likelyhood for state " + getLabel() + " do not sum to 1");
		}
	}

}
